package logic

import (
	"context"
	"time"

	myutils "googee/common/utils"
	"googee/service/leaderboard/model"
	"googee/service/leaderboard/rpc/internal/svc"
	"googee/service/leaderboard/rpc/internal/utils"
	"googee/service/leaderboard/rpc/types/leaderboard"

	"github.com/fatih/color"
	"github.com/jinzhu/copier"
	"github.com/zeromicro/go-zero/core/logx"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

type GetRankLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewGetRankLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetRankLogic {
	return &GetRankLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

//  获得排行榜数据
func (l *GetRankLogic) GetRank(in *leaderboard.RankRequest) (resp *leaderboard.RankResponse, err error) {

	limit := in.GetLimit()
	offset := in.GetOffset()
	dense := in.GetDense()

	//获得排行榜对象
	board, err := l.svcCtx.LeaderboardsModel.FindOne(l.ctx, in.Bid)
	if err != nil || board == nil {
		return nil, status.Error(codes.NotFound, "没有找到排行榜")
	}

	now := time.Now()
	if in.Batchid > 0 {
		now = time.Unix(in.Batchid, 0)
	}
	var batchid int64 = 1

	if board.Typ == "N" || board.Cron == "" {
		batchid = 1
	} else {
		prev := myutils.PrevCronTime(board.Cron, now)
		color.Red("prev=%v", prev)
		for i := 0; i < myutils.AbsInt(int(in.BatchOffset)); i++ {
			if in.BatchOffset < 0 {
				prev = myutils.PrevCronTime(board.Cron, prev.Add(-1*time.Second))
			} else {
				prev = myutils.NextCronTime(board.Cron, prev.Add(1*time.Second))
			}
			color.Red("prev=%v", prev)
		}
		batchid = prev.Unix()
	}

	resp = &leaderboard.RankResponse{}

	resp.Batchid = batchid

	color.Green("batchid=%v", batchid)

	const ZERO int64 = 0
	// from_score, to_score, from_rank, to_rank = db.query_one('SELECT from_score, to_score, from_rank, to_rank FROM chunk_buckets WHERE lid=%s AND from_rank<=%s AND %s<=to_rank', (leaderboard_id, offset+1, offset+1))
	querysql := "SELECT * FROM leaderboards_buckets WHERE bid=? AND from_rank<=? AND ?<=to_rank and batchid=?"

	from_score, to_score, from_rank, to_rank := ZERO, ZERO, ZERO, ZERO

	var buck model.LeaderboardsBuckets
	err = l.svcCtx.SqlConn.QueryRowCtx(l.ctx, &buck, querysql, in.Bid, offset+1, offset+1, batchid)
	if err != nil {
		color.Red("err1=%v", err)
		return resp, nil
	}

	from_score = buck.FromScore
	to_score = buck.ToScore
	from_rank = buck.FromRank
	to_rank = buck.ToRank

	if to_rank < limit+offset+1 {
		querysql = `
			SELECT * FROM leaderboards_buckets WHERE bid=? AND from_rank<=? AND ?<=to_rank and batchid=?
		`
		err = l.svcCtx.SqlConn.QueryRowCtx(l.ctx, &buck, querysql, in.Bid, limit+offset+1, limit+offset+1, batchid)
		if err == nil {
			from_score = buck.FromScore
		}
	}

	sql := "SELECT * FROM leaderboards_data WHERE bid=? AND ?<=score AND score<=? and batchid=? "
	if dense {
		sql += "ORDER BY score DESC, user_id ASC"
	} else {
		// sql += "GROUP BY score, user_id ORDER BY score DESC"
		sql += "ORDER BY score DESC, user_id ASC"
	}

	sql += " LIMIT ? OFFSET ?"

	var buckdata []*model.LeaderboardsData
	err = l.svcCtx.SqlConn.QueryRowsCtx(l.ctx, &buckdata, sql, in.Bid, from_score, to_score, batchid, limit, offset-from_rank+1)
	if err != nil {
		return resp, nil
	}

	resp.Batchid = batchid

	if len(resp.Data) >= 0 {
		copier.Copy(&resp.Data, buckdata)
		var rank int64
		if !dense {
			rank, err = utils.RankForUser(l.svcCtx.SqlConn, l.ctx, in.GetBid(), buckdata[0].UserId, batchid, dense)

			if err != nil {
				color.Red("err rank for user:%v", err)
				return resp, nil
			}
			offset = rank
		} else {
			offset += 1
		}
		logx.Infof("rank:%v", rank)

		prev_entry := resp.Data[0]
		prev_entry.Rank = offset

		for i, d := range resp.Data {
			if i > 0 {
				if dense {
					offset += 1
				} else if d.Score != prev_entry.Score {
					offset += 1
				}
				d.Rank = offset
				prev_entry = d
			}
		}
	}

	return resp, nil
}
